#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <linux/input.h>
#include <sys/types.h>
#include <pthread.h>
#include "InCommon.h"
#include "InPerLed.h"
#include "InSdkTcpSystemClient.h"
#include "InPerKey.h"
#include "InSdkCallbackFunction.h"
#include "InSdkSoundPlay.h"

#define DEF_TAG             "Key"

#if CMAKE_INGENIC_T41N
#define DEF_KEY_GPIO_DEV        "/dev/input/event0"
#else
#define DEF_KEY_GPIO_DEV        "/dev/event0"
#endif

#define DEF_POWER_OFF_TIME      3
#define DEF_RESET_TIME          6

typedef	enum
{
    KEY_STAT_UP = 0,
    KEY_STAT_DOWN,
}EPerKeyStatus;

extern Com_void Ext_InPer_UartSend_Reset(Com_void);

static EPerKeyStatus g_InPer_Key_0_Status = KEY_STAT_UP;
static EPerKeyStatus g_InPer_Key_1_Status = KEY_STAT_UP;
static struct timeval g_InPer_Key_Stime, g_InPer_Key_Etime;
static Com_u8 g_InPer_Key_Reset_Flag = DEF_COM_FALSE;
static Com_u8 g_InPer_PowerOff_Flag = DEF_COM_FALSE;

//返回毫秒
static double InPer_Key_Difftimeval (struct timeval *old, struct timeval *new)
{
    return (new->tv_sec - old->tv_sec) * 1000 + (new->tv_usec - old->tv_usec) / 1000;
}

static int InPer_Key_0_Handle(double period)
{
    if (period < 0)
    {
        perror("Error period");
        return -1;
    }
    else if (period < 1)
    {
        /* 单击按键 */
    }
    else if (period < DEF_RESET_TIME)
    {
        //PRT_INFO(DEF_TAG,"Invalid trigger !\n");
    }
    else if (period >= DEF_RESET_TIME)
    {
        if(g_InPer_Key_Reset_Flag == 1)
        {
            g_InPer_Key_Reset_Flag = 0;

            Ext_InPer_UartSend_Reset();
            Ext_InComFile_Creat(DEF_FLAG_RESTORE_FACTORY);
            Ext_InSdkSpkPlay_WavInit(DEF_WAV_DING_DONG);
            sleep(3);
            PRT_INFO(DEF_TAG,"system factory reset !\n");
            Ext_InSdkTcpSystem_Cmd("reboot");
        }
    }

    return 0;
}

static int InPer_Key_0_Level_Detect(struct input_event *event)
{
    double period = 0;
    int trigger_val = 0;

    if (event->value == trigger_val)
    {
        g_InPer_Key_Stime.tv_sec = event->time.tv_sec;
        g_InPer_Key_Stime.tv_usec = event->time.tv_usec;
        g_InPer_Key_0_Status = KEY_STAT_DOWN;
    }
    else if (event->value == (1 - trigger_val))
    {
        period = InPer_Key_Difftimeval(&g_InPer_Key_Stime, &event->time) / 1000;

        InPer_Key_0_Handle(period);
        g_InPer_Key_0_Status = KEY_STAT_UP;
        return 0;
    }

    return 1;
}

static int InPer_Key_1_Handle(double period)
{
    if (period < 0)
    {
        perror("Error period");
        return -1;
    }
    else if (period < 1)
    {
        /* 单击按键 */
    }
    else if (period < DEF_POWER_OFF_TIME)
    {
        //PRT_INFO(DEF_TAG,"key1 invalid trigger !\n");
    }
    else if (period >= DEF_POWER_OFF_TIME)
    {
    }

    return 0;
}

static int InPer_Key_1_Level_Detect(struct input_event *event)
{
    int trigger_val = 0;
    double period = 0;

    if (event->value == trigger_val)
    {
        g_InPer_Key_Stime.tv_sec = event->time.tv_sec;
        g_InPer_Key_Stime.tv_usec = event->time.tv_usec;
        g_InPer_Key_1_Status = KEY_STAT_DOWN;
    }
    else if (event->value == (1 - trigger_val))
    {
        period = InPer_Key_Difftimeval(&g_InPer_Key_Stime, &event->time) / 1000;
        // printf("period = %f(s).\n", period);
        InPer_Key_1_Handle(period);
        g_InPer_Key_1_Status = KEY_STAT_UP;

        return 0;
    }

    return 0;
}

static int InPer_Key_Detect(struct input_event *key_event, int key_cnt)
{
    int i = 0;
    int ret = -1;
    struct input_event *event;

    if (key_cnt < (int) sizeof(struct input_event)) {
        printf("expected %d bytes, got %d\n", (int) sizeof(struct input_event), key_cnt);
        return -1;
    }

    for (i = 0; (i < key_cnt/sizeof(struct input_event)); i++)
    {
        event = (struct input_event *)&key_event[i];
        if (EV_KEY != event->type)
        {
            continue;
        }

//        PRT_NORMAL("code = %d, value = %d!\n",event->code, event->value);

        switch(event->code)
        {
        case KEY_0:
            ret = InPer_Key_0_Level_Detect(event);
            break;
        case KEY_1:
            ret = InPer_Key_1_Level_Detect(event);
            break;
        default:
            printf("%s: Error key code!\n", __func__);
            ret = -1;
            break;
        }

        if (!ret)
        {
            break;
        }
    }
    return ret;
}

void *InPer_Key_Thread(void *argc)
{
    int ret = 0;
    int gpio_fd;
    int rd = 0;
    struct input_event key_event[4];
    fd_set readfds;
    struct timeval timeout;
    double current_DEF_RESET_TIME = 0;

    memset(&g_InPer_Key_Stime,0,sizeof(g_InPer_Key_Stime));
    memset(&g_InPer_Key_Etime, 0, sizeof(g_InPer_Key_Etime));

    if ((gpio_fd = open(DEF_KEY_GPIO_DEV, O_RDONLY)) < 0)
    {
        perror("Open gpio key dev fail");
        return NULL;
    }

    DEF_COM_SET_THREAD_NAME("PerKey",g_ComThread_Num);

    while(g_ComThread_Running)
    {
        FD_ZERO(&readfds);
        FD_SET(gpio_fd, &readfds);

        timeout.tv_sec = 1;
        timeout.tv_usec = 0;

        ret = select(FD_SETSIZE, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval*)&timeout);
        if (ret < 0)
        {
            //perror("select error\n");
        }
        else if (ret == 0)
        {
            //timeout
            if (g_InPer_Key_0_Status == KEY_STAT_DOWN)
            {
                gettimeofday(&g_InPer_Key_Etime, NULL);
                //printf("g_InPer_Key_Etime's sec= %d, nsec= %d !\n", g_InPer_Key_Etime.tv_sec, g_InPer_Key_Etime.tv_usec);
                PRT_INFO(DEF_TAG,"sec = %f(s)\n",InPer_Key_Difftimeval(&g_InPer_Key_Stime, &g_InPer_Key_Etime) / 1000);
                current_DEF_RESET_TIME = InPer_Key_Difftimeval(&g_InPer_Key_Stime, &g_InPer_Key_Etime) / 1000;

                if ((InPer_Key_Difftimeval(&g_InPer_Key_Stime, &g_InPer_Key_Etime) / 1000) > DEF_RESET_TIME)
                {
                    g_InPer_Key_0_Status = KEY_STAT_UP;
                    PRT_NORMAL("reset config\n");
                    g_InPer_Key_Reset_Flag = 1;
                    InPer_Key_0_Handle(current_DEF_RESET_TIME);
                }
            }

            if (g_InPer_Key_1_Status == KEY_STAT_DOWN)
            {
                gettimeofday(&g_InPer_Key_Etime, NULL);
                //printf("g_InPer_Key_Etime's sec= %d, nsec= %d !\n", g_InPer_Key_Etime.tv_sec, g_InPer_Key_Etime.tv_usec);
                PRT_INFO(DEF_TAG,"pwr off sec = %f(s)\n",InPer_Key_Difftimeval(&g_InPer_Key_Stime, &g_InPer_Key_Etime) / 1000);
                current_DEF_RESET_TIME = InPer_Key_Difftimeval(&g_InPer_Key_Stime, &g_InPer_Key_Etime) / 1000;
                if ((InPer_Key_Difftimeval(&g_InPer_Key_Stime, &g_InPer_Key_Etime) / 1000) > DEF_POWER_OFF_TIME)
                {
                    g_InPer_Key_1_Status = KEY_STAT_UP;
                    PRT_INFO(DEF_TAG,"device power off now... \n");
                    g_InPer_PowerOff_Flag = DEF_COM_TRUE;
                    InPer_Key_1_Handle(current_DEF_RESET_TIME);
                }
            }
        }
        else
        {
            if (FD_ISSET(gpio_fd, &readfds))
            {
                rd = read(gpio_fd, key_event, sizeof(struct input_event) * sizeof(key_event));
                InPer_Key_Detect(key_event, rd);
            }
        }
    }

    DEF_COM_EXIT_THREAD(g_ComThread_Num);
}

Com_void Ext_InPer_Key_Init(Com_void)
{
    pthread_t id;
    pthread_create(&id, NULL,InPer_Key_Thread, NULL);
    pthread_detach(id);
}
